﻿/**
 *
 * detector.c
 *
 *  Created on: 2019-11-29
 *      Author: chenshisheng
 */

#include "detector.h"
#include "hw.h"
#include "store.h"
#include "load.h"
#include "battery.h"
#include "power_manager.h"
#include "ctimer.h"
#include "resource.h"
#include "speaker.h"
#include "at_command.h"
#include "door.h"
#include "net.h"

#include <math.h>
#include <stdlib.h>

#define STATUS_NOT_CALIBRATED 0x01
#define STATUS_SENSOR_1_ERROR (0x01 << 1)
#define STATUS_SENSOR_2_ERROR (0x01 << 2)
#define STATUS_SENSOR_3_ERROR (0x01 << 3)
#define STATUS_SENSOR_4_ERROR (0x01 << 4)
#define STATUS_ERROR          (0x01 << 5)

static Ctimer_t _deliverTimer, _heartbeatTimer, _netActiveTimer;

static struct
{
    float total;
    float last;
    float deliver;
    bool isReachEnd;
}_weighDetector;

static bool _forceUpload;
static int _batteryPercent;
static bool _isFull;
static bool _isSensorFail;

static void _OnDeliver(int data);

void Detector_Init(void)
{
	_forceUpload = true;
	_isSensorFail = false;
    _batteryPercent = 0;
    _isFull = false;
    memset(&_weighDetector, 0, sizeof(_weighDetector));

    HW_SetExtiCallback(HW_Exti_Deliver1, _OnDeliver, 0);
    HW_SetExtiCallback(HW_Exti_Deliver2, _OnDeliver, 1);

    // 上电后进行一次常规检测
    MsgQueue_Send(MsgQueue_Id_RoutineDetect, NULL, 0);
}

static bool _FullDetect(void)
{
    bool isFull;

    HW_Config(HW_Out_IrEn, HW_State_Active);
    HAL_Delay(20);
    isFull = HW_IsActive(HW_In_IrIn) ? true : false;
    HW_Config(HW_Out_IrEn, HW_State_Inactive);

    return isFull;
}

static void _OnNewDeliver(int data)
{
    Load_Info_t current;
    AtCommand_Value_t value;
    int ret;
    bool isSensorFail;

    ret = Load_GetInfo(&current, NULL);
    if((ret != 0) || (current.statusBits != 0))
    {
        // 标识称重传感器异常
        isSensorFail = true;
        debug("Detector: load sensor error! ret = %d, status =  0x%02x\n",
                ret, current.statusBits);
    }
    else
    {
        isSensorFail = false;
    }

    if(isSensorFail != _isSensorFail)
    {
        value.bv = isSensorFail;
        Resource_Update(Resource_IsSensorFail, AtCommand_DataType_Bool, value);
        _isSensorFail = isSensorFail;
    }

    // 当连接不上称重板或个别传感器 出现错误表示没有重量值
    // 而称重板未校准时的重量值强可勉强一用
    if((ret != 0) || (current.statusBits > STATUS_NOT_CALIBRATED))
    {
        return;
    }

    if(fabsf(current.weight - _weighDetector.total) > 0.05)
    {
        value.fv = current.weight;
        Resource_Update(Resource_TotalWeight, AtCommand_DataType_Float, value);
        _weighDetector.total = current.weight;
        debug("Detector: total weight = %.3fKg\n", value.fv);
    }

    value.fv = current.weight - _weighDetector.last;
    if(value.fv > 0.05)
    {
        Resource_Update(Resource_InputWeight, AtCommand_DataType_Float, value);
        debug("Detector: deliver weight = %.3fKg\n", value.fv);
    }
}

static void _OnHeartbeat(int data)
{
    Net_MiplUpdate(NET_LIFETIME);
}

static void _OnNetActiveTimeout(int data)
{
    Ctimer_Stop(&_heartbeatTimer);
}

static void _OnDeliver(int data)
{
    PowerManager_ExitLowPowerMode();
    if(data == 0)
    {
        _weighDetector.last = _weighDetector.total;
        _weighDetector.isReachEnd = false;
        Ctimer_Start(&_heartbeatTimer, 5 * CTIMER_TICK_SECOND, _OnHeartbeat, 0, 0);
        Ctimer_Start(&_netActiveTimer, 3 * CTIMER_TICK_MINUTE, _OnNetActiveTimeout,
                CTIMER_FLAG_ONCE, 0);
        Speaker_SpeakAfter(2);
    }
    else
    {
        _weighDetector.isReachEnd = true;
        Ctimer_Start(&_deliverTimer, 3000, _OnNewDeliver, CTIMER_FLAG_ONCE, 0);
    }
}

void Detector_OnNotified(const MsgQueue_Msg_t *msg)
{
    debug("Notified\n");
}

void Detector_OnDoorAction(const MsgQueue_Msg_t *msg)
{
    AtCommand_Value_t value;

    value.bv = *((bool *)msg->data);
    Resource_Update(Resource_IsDoorOpened, AtCommand_DataType_Bool, value);
    if(! value.bv)
    {
        // 取物门关闭，说明已经取走织物，则需要更新重量值
        MsgQueue_Send(MsgQueue_Id_RoutineDetect, NULL, 0);
    }
}

void Detector_OnRoutine(const MsgQueue_Msg_t *msg)
{
    int per;
    AtCommand_Value_t value;
    Load_Info_t info;
    bool isFull, isSensorFail;
    int ret;

    debug("Detector: routine\n");
    ret = Battery_VoltageMeasure(NULL, &per);
    if(ret != 0)
    {
         // TODO 电池异常处理
        debug("Detector: battery error: %d!\n", ret);
    }

    if(abs(per - _batteryPercent) > 3)
    {
        value.fv = per;
        Resource_Update(Resource_Battery, AtCommand_DataType_Float, value);
        _batteryPercent = per;
    }

    if(Load_GetInfo(&info, NULL) == 0)
    {
        if(info.statusBits == 0)
        {
        	isSensorFail = false;
            if(fabsf(info.weight - _weighDetector.total) > 0.05)
            {
                value.fv = info.weight;
                Resource_Update(Resource_TotalWeight, AtCommand_DataType_Float, value);
                _weighDetector.total = info.weight;
            }
        }
        else
        {
        	isSensorFail = true;
            debug("Detector: load sensor error! status =  0x%02x\n", info.statusBits);
        }
    }
    else
    {
    	isSensorFail = true;
        debug("Detector: Can not connect to load sensor board!\n");
    }

    if((isSensorFail != _isSensorFail) || _forceUpload)
    {
    	value.bv = isSensorFail;
        Resource_Update(Resource_IsSensorFail, AtCommand_DataType_Bool, value);
    	_isSensorFail = isSensorFail;
    }

    isFull = _FullDetect();
    if((isFull != _isFull) || _forceUpload)
    {
        value.bv = isFull;
        Resource_Update(Resource_IsFull, AtCommand_DataType_Bool, value);
        _isFull = isFull;
    }

    if(_forceUpload)
    {
        value.bv = Door_IsOpened();
        Resource_Update(Resource_IsDoorOpened, AtCommand_DataType_Bool, value);
    }

    _forceUpload = false;
}
